﻿using System;
using System.Configuration.Install;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Security.Principal;
using System.ServiceProcess;
using Autofac;
using BuildBox.Core;

namespace BuildBox.Server
{
    public static class Program
    {
        private static IDependencyResolver _dependencyResolver;
        private static IWindowsIdentityAdapter _identity;

        public static void Main(string[] args)
        {
            _dependencyResolver = new DependencyResolver(new ConfigurationModule());
            _identity = _dependencyResolver.Container.Resolve<IWindowsIdentityAdapter>();

            if (Environment.UserInteractive)
            {
                RunAsInteractive(args);
            }
            else
            {
                MainService service = _dependencyResolver.Container.Resolve<MainService>();
                ServiceBase.Run(new[] { service });
            }
        }

        public static void RunAsInteractive(string[] args)
        {
            switch (ArgumentParser.GetArgument(args))
            {
                case "console":
                    RunAsConsole();
                    break;
                case "install":
                    AdminRequired(InstallService, "/install");
                    break;
                case "uninstall":
                    AdminRequired(UninstallService, "/uninstall");
                    break;
                default:
                    DisplayUsage();
                    break;
            }
        }

        public static void RunAsConsole()
        {
            using (BuildBoxServer server = _dependencyResolver.Container.Resolve<BuildBoxServer>())
            {
                server.SetupForNonAdmin();
                server.Start();
                do
                {
                    Console.WriteLine("Server is running...");
                    Console.WriteLine("Press enter key to stop server. Type 'cls' and press enter key to clear log.");

                    string input = Console.ReadLine();
                    if (string.Compare(input, "cls", true) != 0)
                    {
                        break;
                    }

                    Console.Clear();
                }
                while (true);
            }
        }

        public static void InstallService()
        {
            if (ServiceIsInstalled())
            {
                Console.WriteLine("The service is already installed.");
                return;
            }

            ManagedInstallerClass.InstallHelper(new[] { Assembly.GetExecutingAssembly().Location });
        }

        public static void UninstallService()
        {
            ManagedInstallerClass.InstallHelper(new[] { "/u", Assembly.GetExecutingAssembly().Location });
        }

        public static void DisplayUsage()
        {
            Console.WriteLine(
@"BuildBox Server
A Build Server for Windows
http://buildbox.codeplex.com
----------------------------

Usage: 
   BuildBox.Server.exe               Runs BuildBox in console mode
   BuildBox.Server.exe /help         Displays Usage
   BuildBox.Server.exe /install      Installs BuildBox as a Windows Service
   BuildBox.Server.exe /uninstall    Uninstalls the BuildBox Windows Service");
        }

        private static bool ServiceIsInstalled()
        {
            return ServiceController.GetServices().Count(s => s.ServiceName == MainInstaller.ServiceName) > 0;
        }

        private static void AdminRequired(Action actionThatMayRequiresAdminPrivileges, string cmdLine)
        {
            var principal = new WindowsPrincipal(_identity.Identity);
            if (principal.IsInRole(WindowsBuiltInRole.Administrator) == false)
            {
                if (RunAgainAsAdmin(cmdLine))
                {
                    return;
                }
            }

            actionThatMayRequiresAdminPrivileges();
        }

        private static bool RunAgainAsAdmin(string cmdLine)
        {
            try
            {
                var process = Process.Start(new ProcessStartInfo
                                                {
                                                    Arguments = cmdLine,
                                                    FileName = Assembly.GetExecutingAssembly().Location,
                                                    Verb = "runas",
                                                });

                if (process != null)
                {
                    process.WaitForExit();
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
